﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using cn.edu.suda.sumcu.iot;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Reflection;
//using SerialPort;
using System.Diagnostics;
using System.Timers;
using System.Net.NetworkInformation;
using cn.edu.suda.sumcu.iot.util;
using RemoteUpdate;
using Easily;

namespace AHL_GEC
{
    public partial class frm_NBUpdate : Form
    {
        //定义使用的全局变量
        private Hex hex;  //Hex文件信息，整体更新用
        private Update update;  //更新类，保存更新所使用的帧结构体与方法
        private SynchronizationContext m_SyncContext = null;    //用于安全地跨线程访问控件
        private uint softVersion; //保存终端更新程序版本，0表示更新版本不详，1表示更新版本为VA.10之前，2表示更新版本为VA.10之后
        private static UInt32 message_count;
        private List<FileInfo> fileList;    //当前工程文件列表
        string modulePath = string.Empty;    //模板工程路径
        string[] IMSI = new string[10];
        private int NBsum;          //发送总帧数
        private int NBcur;          //当前发送帧号

        int link_gec = 0;
        int back_user = 0;
        int update_button = 0;

        public HCICom com = new HCICom();    //用于侦听发送数据的通讯对象
        public HCICom updateCom = new HCICom();    //【20220719】用于程序更新的通讯对象
        int updata_flag = 0;

        string imsi1;
        byte[] head;
        int dc_num; //等于总帧数，用于下位机返回的帧数统计数组判断
        byte[] dc_send1;
        int reask = 0;
        int dc_len = 0;
        private bool closing = false;      //标识是否正在关闭串口
        private bool listening = false;    //标识是否执行完invoke相关操作
        //可能需要重发的数组及长度
        byte[] resenddata = new byte[1024];
        int resenddatalen = 0;


        int BIOS_USER_updataStyle;           //更新类型选择 1：User更新 0：BIOS更新
        uint UserStartAddress;
        int countDownSec = 0;  //记录倒计时秒数

        //握手帧
        // LayoutKind.Sequential用于强制将成员按其出现的顺序进行顺序布局,字符串转换成ANSI字符串，Pack表示1字节对齐
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct shakeData
        {
            // SizeConst用来定义数组大小
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public byte[] uecomType;          //通信模组类型
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
            public byte[] mcuType;            //MCU类型
            public uint uStartAds;            //User程序起始地址
            public uint uCodeSize;            //User程序总代码大小
            public uint replaceNum;           //替换更新最大字节
            public uint reserveNum;           //保留更新最大字节（不等于0意味着有User程序）
        }
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct newshakeData
        {
            // SizeConst用来定义数组大小
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public byte[] uecomType;          //通信模组类型
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public byte[] mcuType;            //MCU类型
            public uint uStartAds;            //User程序起始地址
            public uint uCodeSize;            //User程序总代码大小
            public uint replaceNum;           //替换更新最大字节
            public uint reserveNum;           //保留更新最大字节（不等于0意味着有User程序）
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
            public byte[] BIOSVersion;            //BIOS版本号
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
            public byte[] USERVersion;            //BIOS版本号
        }
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct new2shakeData
        {
            // SizeConst用来定义数组大小
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public byte[] uecomType;          //通信模组类型
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public byte[] mcuType;            //MCU类型
            public uint uStartAds;            //User程序起始地址
            public uint uCodeSize;            //User程序总代码大小
            public uint replaceNum;           //替换更新最大字节
            public uint reserveNum;           //保留更新最大字节（不等于0意味着有User程序）
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public byte[] BIOSVersion;            //BIOS版本号
            public uint RAMStart;                //RAM起始地址
            public uint RAMLength;               //RAM长度
            public uint FlashStart;              //Flash起始地址
            public uint FlashLength;             //Flash长度
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public byte[] coreType;             //内核类型
            public uint mcuSectSize;             //mcu扇区大小
            public uint RTOSSize;             //操作系统空间大小
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public byte[] RTType;             //内核类型
            public uint RAMUserStart;             //RAM空间User的起始地址
            public uint SystemClock;             //RAM空间User的起始地址
        }
        private new2shakeData new2shakedata;
        private newshakeData newshakedata;
        private string uecomType;   //通信模组类型
        private string mcuType;     //MCU类型
        private string mcuType_static = "STM32L431BIOS";     //MCU类型
        public string textIDE = "KDS";
        private uint uStartAds;     //User程序起始地址
        private uint uCodeSize;     //User程序总代码大小
        private uint replaceNum;    //替换更新最大字节
        private uint reserveNum;    //保留更新最大字节（不等于0意味着有User程序）
        private byte overallStyle;  //整体更新方式
        private string biosVersion; //BIOS版本号 
        private string biosType; //BIOS版本号
        private uint sectorStart;
        private uint ramStart;       //ram起始地址
        private uint ramLength;      //ram长度
        private uint flashStart;      //flash起始地址
        private uint flashLength;     //flash长度
        private string coreType;      //内核类型
        private uint mcuSectSize;   //扇区大小
        private string rtType;     //MCU类型
        private uint rtOSSize;   //操作系统空间大小
        private uint ramUserStart;   //RAM空间User的起始地址
        private string userVersion; //USER版本号
        private bool gecLinkEnable = true; //是否运行连接GEC，true:可以连接，false：禁止连接

        int socket_flag = 0;


        //相关定时器
        System.Timers.Timer t_first_send_back = new System.Timers.Timer(6000);  //先行帧定时器
        //【20220719】
        System.Timers.Timer t_countdown_S = new System.Timers.Timer(1000); //倒计时定时器

        System.Timers.Timer pingInter = new System.Timers.Timer(1000 * 60); //联网定时器


        public delegate void DelReadStdOutput(string result);
        public delegate void DelReadErrOutput(string result);
        public event DelReadStdOutput ReadStdOutput;
        public event DelReadErrOutput ReadErrOutput;

        public static UInt32[] crcTable =
{
          0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
          0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
          0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
          0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
          0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
          0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
          0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
          0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
          0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
          0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
          0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
          0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
          0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
          0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
          0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
          0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
          0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
          0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
          0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
          0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
          0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
          0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
          0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
          0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
          0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
          0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
          0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
          0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
          0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
          0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
          0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
          0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
        };
        //======================================================================
        //函数名称：frm_uartUpdate
        //函数返回：frm_uartUpdate窗体构造函数
        //参数说明：无
        //功能概要：完成frm_uartUpdate窗体的初始化工作
        //======================================================================
        public frm_NBUpdate()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;
        }

        //======================================================================
        //函数名称：frm_uartUpdate_Load
        //函数返回：无
        //参数说明：无
        //功能概要：窗体加载事件，窗体加载时自动调用
        //======================================================================
        private void frm_uartUpdate_Load(object sender, EventArgs e)
        {

            //（1）本窗体由frmMain创建，所以本窗体的父窗体为frmMain
            // fmain = FrmMain.getInstance();
            //fmain = (FrmMain)this.MdiParent;  //得到frmMain窗体变量
            m_SyncContext = SynchronizationContext.Current;   //用于安全地跨线程访问控件
            overallStyle = 0;    //更新方式为默认为整体更新方式
            message_count = 0;   //数据接收条数为0
            //（2）初始化Hex变量
            hex = new Hex();
            //（3）初始化串口
            // emuart = EMUART.getInstance();
            //（4）初始化版本号
            softVersion = 0;  //表示版本号不明
            //（5）初始化原更新程序变量           
            //测试网络线程
            pingInter.Elapsed += new System.Timers.ElapsedEventHandler(testNet);//到时间的时候执行事件； 
            pingInter.AutoReset = true;//设置是执行一次（false）还是一直执行(true)； 
            pingInter.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件；
        }


        //======================================================================
        //函数名称：GetCRC32
        //函数返回：无
        //参数说明：bytes：数据数组
        //功能概要：CRC校验
        //======================================================================

        public static byte[] GetCRC32(byte[] bytes)
        {
            uint iCount = (uint)bytes.Length;
            uint crc = 0xFFFFFFFF;

            for (uint i = 0; i < iCount; i++)
            {
                crc = (crc << 8) ^ crcTable[(crc >> 24) ^ bytes[i]];
            }

            byte f = (byte)((crc & 0xFF000000) >> 24);
            byte s = (byte)((crc & 0x00FF0000) >> 16);
            byte t = (byte)((crc & 0x0000FF00) >> 8);
            byte fo = (byte)((crc & 0x000000FF) >> 0);

            return new byte[] { f, s, t, fo };


        }



        ///以下为内部函数
        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 函数名称：testNet                                     
        /// 传入参数：无                                                 
        /// 函数返回：无      
        /// 函数功能：通过ping百度的IP判断是否联网，并弹出MessageBox提示
        /// </summary>                                                                                          
        ///-----------------------------------------------------------------
        public void testNet(object source, System.Timers.ElapsedEventArgs e)
        {
            bool flag = true;
            string message = "";
            int n;
            n = 0;
            try
            {
                Ping p = new Ping();
                for (n = 0; n <= 3; n++)
                {
                    PingReply pr = p.Send("www.baidu.com");  //"180.97.33.108"
                    if (pr.Status == IPStatus.Success) break;
                    if (n == 3) flag = false;
                }
            }
            catch
            {

            }
            if (flag == false)
            {
                txtShow1("当前计算机尚未联网，请联网后重启程序\r\n");     //右侧更新提示区 
            }
            //else
            //{
            //    txtShow1("网络正常\r\n");     //右侧更新提示区 
            //}

        }





        //======================================================================
        //函数名称：btn_fileOpen1_Click
        //函数返回：无
        //参数说明：无
        //功能概要：导入待整体更新Hex文件并对该文件进行解析取出其有效数据
        //======================================================================
        private void openHex()
        {
            //（1）变量声明           
            int flag;
            uint startAdd;            //代码起始地址
            uint codesize;            //代码大小

            string filePath;          //文件路径
            string fileName;          //文件名
            string line;              //Hex文件行数据
                                      // List<hexStruct_old> list; //更新程序对应Hex文件保存变量            
                                      //（2）如果终端更新程序版本为VA.10之后的版本
            if (softVersion == 2)
            {
                string strMakefileHexPath = Globaldefine.exePath + @"Project\" + mcuType_static + "\\" + textIDE;
                if (!File.Exists(strMakefileHexPath + @"\easily.hex"))
                {
                    MessageBox.Show("未找到hex文件，请重新编译！");
                    this.btn_autoupdate1.Enabled = true;
                }
                //（2.1）导入Hex文件
                flag = hex.loadFile(strMakefileHexPath + @"\easily.hex");
                if (flag != 0) goto btn_fileopen1_Click_EXIT1;
                //（2.4）获取导入的文件信息
                fileName = hex.getFileName();
                startAdd = hex.getStartAddress();    //hex文件中读取的起始地址
                codesize = hex.getCodeSize();
                string ldFilePath = string.Empty;
                string userStart = string.Empty;      //被替换的起始地址
                string strStartAdd = string.Empty;    //替换的起始地址
                StreamReader sr = null;
                fileList = new List<FileInfo>();
                //如果打开工程，则先修改link文件中RAM的起始地址

                if (BIOS_USER_updataStyle == 1)
                {
                    //如果是USER更新
                    if (startAdd != uStartAds)
                    {
                        //MessageBox.Show("当前User程序与BIOS程序版本不匹配");
                        this.txt_updateinfo1.Text = "运行状态：当前导入的hex文件无法烧入目标设备";
                        goto btn_fileopen1_Click_EXIT3;
                    }
                }
                //（2.5）判断导入的Hex文件首地址是否正确
                //（2.6）状态提示
                this.prg_update1.Value = 0;
                this.prg_update1.Text = "";
                this.btn_autoupdate1.Enabled = true;  //允许整体更新功能
                txtShow1("导入" + fileName + "文件成功！\r\n");     //右侧更新提示区  
                this.txt_updateinfo1.Text = "运行状态：" + fileName + "文件选择成功，文件数据解析成功";   //底部提示
            }

        //（4）退出区
        //（4.1）退出函数
        btn_fileopen1_Click_EXIT:
            return;

        //（4.2）导入hex文件失败
        btn_fileopen1_Click_EXIT1:
            this.btn_autoupdate1.Enabled = false;  //禁止整体更新功能
            this.txt_updateinfo1.Text = "运行状态：请重新选择文件";   //底部提示
            MessageBox.Show("Hex文件异常!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            goto btn_fileopen1_Click_EXIT;

        //（4.3）未选择Hex文件
        btn_fileopen1_Click_EXIT2:
            this.txt_updateinfo1.Text = "运行状态：未选择Hex文件";   //底部提示
            goto btn_fileopen1_Click_EXIT;

        //（4.4）导入的Hex文件不合法
        btn_fileopen1_Click_EXIT3:
            this.txt_updateinfo1.Text = "运行状态：请重新选择文件";   //底部提示
            MessageBox.Show("Hex文件异常!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Stop);
            txtShow1("导入的" + fileName + "文件首地址不合法，请检查！\r\n");     //右侧更新提示区  
            this.btn_autoupdate1.Enabled = false;  //不允许整体更新功能
            this.txt_updateinfo1.Text = "运行状态：" + fileName + "文件首地址不合法，请检查！";   //底部提示
            goto btn_fileopen1_Click_EXIT;

        }

        //======================================================================
        //函数名称：btn_autoUpdate1_Click
        //函数返回：无
        //参数说明：无
        //功能概要：“一键自动更新”（整体更新）按钮点击事件，进行程序更新操作  发送先行帧数据
        //======================================================================
        private void btn_autoupdate1_Click(object sender, EventArgs e)
        {
            //1、获取并解析hex文件
            openHex();
            //2、更新开始
            update_start();
        }

        private void update_start()
        {
            //【20220719】新增倒计时
            //可以点击“一键自动更新”，说明连接成功，准备更新
            //解绑倒计时提示
            t_countdown_S.Elapsed -= new System.Timers.ElapsedEventHandler(countDown);//到时间的时候执行事件； 
            countDownSec = 0; //清除倒计时结束
            lblCountDown.Text = "倒计时提示：关闭";
            //（1）变量声明
            byte[] senddata = null;
            Application.DoEvents();     //【20191024】与重新连接部分原理相同
            btn_autoupdate1.Enabled = false;
            updata_flag = 1;

            NBcur = 0;
            //（2.1）若未导入Hex文件或串口连接失败则退出（防错用）
            if (hex.getHexList().Count == 0) return;
            //（2.2）开始更新
            update = new Update(overallStyle, hex.getHexList());  //update初始化
            NBsum = update.getFrameNum();
            //this.txt_updateinfo1.Text = "运行状态：整体更新开始";   //底部提示
            txtShow1("运行状态：整体更新开始\r\n");     //右侧更新提示区  
            //（2.3）发送先行帧，使得下位机擦除原有USER，做好接收数据的准备
            if ((senddata = update.getNextFrame()) != null)
            {
                byte[] newsenddata = new byte[senddata.Length + 2 + 1];
                //byte[] newsenddata = new byte[60];
                Array.Copy(senddata, 0, newsenddata, 3, senddata.Length);
                newsenddata[0] = (byte)'F';  //先行帧标志位
                newsenddata[1] = (byte)(update.getFrameNum() - 2); //总帧数
                if (BIOS_USER_updataStyle == 1) //选择更新类型
                {
                    newsenddata[2] = 1;
                }
                else
                    newsenddata[2] = 0;
                Array.Copy(newsenddata, 0, resenddata, 0, newsenddata.Length);
                resenddatalen = newsenddata.Length;
                //发送先行帧
                //【20220719】用于程序更新的通讯对象
                updateCom.Send(imsi1, newsenddata);
                Thread.Sleep(5000);
                //updateCom.Send(imsi1, newsenddata);

                btn_uartcheck.Enabled = false; //禁用“连接GEC”按钮
                //开启定时器，准备接收下位机先行帧反馈数据
                t_first_send_back.Elapsed += new System.Timers.ElapsedEventHandler(first_send_back);//到时间的时候执行事件； 
                t_first_send_back.AutoReset = false;//设置是执行一次（false）还是一直执行(true)； 
                t_first_send_back.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件；
                return;
            }

        }


        //======================================================================
        //函数名称：txtShow_rece_data
        //函数返回：无
        //参数说明：str:在终端执行信息提示框追加显示的内容
        //功能概要：整体更新过程中，在终端执行信息提示框txt_uartinfo1中追加提示
        //          信息，并显示最新信息      
        //======================================================================
        byte lastCH = 0;    //【20190506】-2/3
        StringBuilder MyStringBuilder = new StringBuilder("", 500); //【20200428】优化效率
        private void txtShow_rece_data(byte[] data)
        {
            int count = 0;
            int i;
            int j;
            byte[] showData = data;   //临时数组

            //【20190506】-3/3 ---解决汉字显示乱码问题
            //如果上一次发送出现汉字断码
            if (lastCH != 0)
            {
                showData = new byte[data.Length + 1];   //临时数组增1一个字节
                showData[0] = lastCH;                   //上次保留的字节填入前端
                Array.Copy(data, 0, showData, 1, data.Length);  //其他数据填充后部
                lastCH = 0;                                     //lastCH清0
                data = showData;                                //指针等同                    
            }
            //从后向前统计，
            while ((count < data.Length) && (data[data.Length - 1 - count] > 128))
            {
                count++;
            }
            //如果连续大于128的字节数为奇数，则将最后一位保存，与下一次的数据一起组合
            if (count % 2 == 1)
            {
                showData = new byte[data.Length - 1];
                Array.Copy(data, 0, showData, 0, data.Length - 1);
                lastCH = data[data.Length - 1];
            }

            //将字符串转为汉字
            //string str = Encoding.GetEncoding("GBK").GetString(showData);
            MyStringBuilder.Append(Encoding.GetEncoding("GBK").GetString(showData));//【20200428】优化效率
            //右侧更新提示区
            //【20191024】删除原来超过220行自动清除文本框的功能，原本清除文本框功能会影响事件响应
            //this.txt_updateinfo1.AppendText(str);
            this.txt_updateinfo1.AppendText(MyStringBuilder.ToString());//【20200428】优化效率
            MyStringBuilder.Clear();


            this.txt_updateinfo1.SelectionStart = this.txt_updateinfo1.Text.Length;  //光标指向最后一位
            this.txt_updateinfo1.ScrollToCaret();   //移动到光标处

        }

        //======================================================================
        //函数名称：txtShow1
        //函数返回：无
        //参数说明：str:在终端执行信息提示框追加显示的内容
        //功能概要：整体更新过程中，在终端执行信息提示框txt_uartinfo1中追加提示
        //          信息，并显示最新信息      
        //======================================================================
        private void txtShow1(string str)
        {
            //右侧更新提示区
            if (this.txt_updateinfo1.Lines.Length >= 220) this.txt_updateinfo1.Text = String.Empty; //长度过长则清空内容
            this.txt_updateinfo1.Text += str;
            this.txt_updateinfo1.Refresh();
            this.txt_updateinfo1.SelectionStart = this.txt_updateinfo1.Text.Length;  //光标指向最后一位
            this.txt_updateinfo1.ScrollToCaret();   //移动到光标处

        }

        //======================================================================
        //函数名称：bytesToStruct
        //函数返回：byte数组转换为对应的结构体
        //参数说明：bytes:字节数组;type:结构体类型
        //功能概要：将byte字节数组数据转换为对应的结构体数据
        //======================================================================
        private object bytesToStruct(byte[] bytes, Type type)
        {
            //（1）变量声明
            int size;
            object obj;
            IntPtr structPtr;

            size = Marshal.SizeOf(type);
            //（2）判断字节长度
            if (size > bytes.Length) return null;
            //（3）分配结构体内存空间
            structPtr = Marshal.AllocHGlobal(size);
            //（4）将byte数组拷贝到分配好的内存空间
            Marshal.Copy(bytes, 0, structPtr, size);
            //（5）将内存空间转换为目标结构体
            obj = Marshal.PtrToStructure(structPtr, type);
            //（6）释放内存空间
            Marshal.FreeHGlobal(structPtr);

            return obj;
        }

        //======================================================================
        //函数名称：DataRecv
        //函数返回：无
        //参数说明：data:待显示数据的二进制形式
        //功能概要：执行异步操作，将接收到的调试数据显示到文本控件上
        //======================================================================
        private void DataRecv(byte[] data)
        {
            //如果正在执行关闭操作
            if (closing)
            {
                return;
            }
            try
            {

                byte[] showData = data;

                message_count++;
                listening = true;    //开始处理数据，可能会用到多线程

                txtShow_rece_data(showData);

                Application.DoEvents();
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                listening = false;
            }
        }

        /// <summary>
        /// 窗体关闭事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frm_uartUpdate_FormClosed(object sender, FormClosedEventArgs e)
        {
            t_first_send_back.Enabled = false;
            if (link_gec == 0 || back_user == 1 || update_button == 1)
            {
                if (socket_flag == 1)
                {
                    com.closelink();
                    updateCom.closelink();
                    socket_flag = 0;
                }
                // CloseFRP();
                this.Dispose();
                this.Close();
            }
            else
            {
                DialogResult diagorel = MessageBox.Show("系统已经在BIOS中，不可退出~\n",
                   "提示", MessageBoxButtons.OK);
            }
        }

        ///-----------------------------------------------------------------                                                      
        /// 函数名:FRPBtnClose_Click                                       
        /// 功  能:关闭frp.exe应用程序            
        /// 参  数:自动生成，函数体内未使用
        /// 返回值:无
        ///-----------------------------------------------------------------
        private void CloseFRP()
        {

            try
            {
                Process[] frpPros = Process.GetProcessesByName("frpc");//获取所有正在运行的frpc.exe进程
                Process[] cmdPros = Process.GetProcessesByName("cmd"); //获取所有正在运行的cmd.exe进程
                //int length = cmdPros.Length;
                if (frpPros.Length > 0 && cmdPros.Length > 0)
                {
                    foreach (Process p in frpPros)
                        p.Kill();                               //关闭正在运行的frpc.exe应用程序
                    foreach (Process p1 in cmdPros)
                        p1.Kill();                              //关闭正在运行的cmd.exe应用程序

                    DialogResult diagorel = MessageBox.Show("frp客户端关闭成功~\n",
                    "提示", MessageBoxButtons.OK);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        ///-----------------------------------------------------------------                                                      
        /// 函数名:btnCloseUpdate_Click                                       
        /// 功  能:关闭更新程序          
        /// 参  数:无
        /// 返回值:无
        ///-----------------------------------------------------------------
        private void btnCloseUpdate_Click(object sender, EventArgs e)
        {
            t_first_send_back.Enabled = false;
            if (link_gec == 0 || back_user == 1 || update_button == 1)
            {
                if (socket_flag == 1)
                {
                    com.closelink();
                    updateCom.closelink();
                    socket_flag = 0;
                }
                //CloseFRP();
                this.Dispose();
                this.Close();
            }
            else
            {
                DialogResult diagorel = MessageBox.Show("系统已经在BIOS中，不可退出~\n",
                   "提示", MessageBoxButtons.OK);
            }
        }

        /// <summary>
        /// 获取工程下所有文件
        /// </summary>
        /// <param name="path">工程路径</param>
        private void GetAllFiles(string path, List<FileInfo> files)
        {
            if (path == null)
            {
                return;
            }
            DirectoryInfo folder = new DirectoryInfo(path);
            FileInfo[] childFiles = folder.GetFiles("*.*");    //文件列表
            foreach (FileInfo childFile in childFiles)
            {
                files.Add(childFile);
            }
            DirectoryInfo[] chldFolders = folder.GetDirectories();    //文件夹列表
            foreach (DirectoryInfo chldFolder in chldFolders)
            {
                GetAllFiles(chldFolder.FullName, files);
            }
        }

        ///-----------------------------------------------------------------                                                      
        /// 函数名:send_update                                       
        /// 功  能:持续发送更新数据         
        /// 参  数:imsi_num：imsi号
        /// 返回值:无
        ///-----------------------------------------------------------------
        public void send_update(string imsi_num)
        {
            byte[] senddata = null; //一帧更新数据数组

            for (; ; )
            {
                //获取当前更新总帧数
                NBsum = update.getFrameNum();
                //上位机更新状态数组清零
                update.nbupdateRecv(NBcur);
                //从update类中取出待发送的数据帧，逐一进行发送
                if ((senddata = update.getNextFrame()) != null)
                {
                    //获取当前要发送的帧数
                    NBcur = update.getNextIndex();
                    if (NBcur < (NBsum - 2))
                    {
                        byte[] newsenddata = new byte[senddata.Length + 3];
                        Array.Copy(senddata, 0, newsenddata, 3, senddata.Length);
                        newsenddata[0] = (byte)'D'; //更新数据帧标识
                        newsenddata[1] = (byte)NBcur;
                        updateCom.Send(imsi_num, newsenddata);
                        this.prg_update1.Value = (NBcur + 1) * 100 / (NBsum - 2);            //进度条显示
                        this.prg_update1.Text = (NBcur + 1) * 100 / (NBsum - 2) + "%";  //进度百分比显示
                        this.prg_update1.Refresh();
                        //成功提示
                        txtShow1("当前第" + (NBcur).ToString() + "/" + (NBsum - 3).ToString() + "帧 \r\n");     //右侧更新提示区
                        Thread.Sleep(2000);

                    }
                    //最后一帧，更新命令帧，发送给下位机之后，等待回发
                    if ((NBcur + 3) == NBsum)
                    {

                        updateCom.DataReceivedEvent -= new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
                        updateCom.DataReceivedEvent += new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
                        txtShow1("正在进行数据检测，请稍等片刻\r\n");     //右侧更新提示区  
                        int time = (NBsum * 5);
                        txtShow1("如果超过" + time.ToString() + "秒无任何提示信息则说明终端出现断电，信号中断等故障，请勿关闭软件，等待终端回发信息\r\n");     //右侧更新提示区  
                        //【20220719】新增倒计时                                                                                       //开启定时器，以便提示用户倒计时
                        countDownSec = time; //设置倒计时为time秒
                        t_countdown_S.Elapsed -= new System.Timers.ElapsedEventHandler(countDown);//先解绑
                        t_countdown_S.Elapsed += new System.Timers.ElapsedEventHandler(countDown);//到时间的时候执行事件； 
                        t_countdown_S.AutoReset = true;//设置是执行一次（false）还是一直执行(true)； 
                        t_countdown_S.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件；
                        break;
                    }

                }
            }
        }

        ///-----------------------------------------------------------------                                                      
        /// 函数名:first_send_back                                       
        /// 功  能:一键更新后的先行帧数据 反馈检测        
        /// 参  数:无
        /// 返回值:无
        ///-----------------------------------------------------------------
        public void first_send_back(object source, System.Timers.ElapsedEventArgs e)
        {
            t_first_send_back.Enabled = false;
            t_first_send_back.Elapsed -= new System.Timers.ElapsedEventHandler(first_send_back);//到时间的时候执行事件； 
            byte[] frameData = null;        //用于存储接收帧中的有效数据
            string imsiRecv = null;
            //【1】判断是否有数据可取，若无数据，退出；若有读取到imsiRecv、frameData中
            if (updateCom.Read(ref imsiRecv, ref frameData) == false)
            {
                //此时若定时器时间到，却未接收到下位机的先行帧反馈再次发送先行帧
                byte[] data = new byte[resenddatalen];
                Thread.Sleep(2000);
                Array.Copy(resenddata, 0, data, 0, resenddatalen);
                updateCom.Send(imsi1, data);
                System.Timers.Timer t1 = new System.Timers.Timer(10000);//实例化Timer类，设置间隔时间为1000毫秒；
                Thread.Sleep(3000);
                t_first_send_back.Elapsed += new System.Timers.ElapsedEventHandler(first_send_back);//到时间的时候执行事件； 
                t_first_send_back.AutoReset = false;//设置是执行一次（false）还是一直执行(true)； 
                t_first_send_back.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件；
                return;
            }
            else
            {
                if (frameData.Length == 1) //backData 帧标识（5）+帧号+状态位
                {
                    //接收到下位机的先行帧反馈，并针对选择的设备进行hex文件数据发送
                    send_update(imsi1);
                }
                return;
            }


        }

        


        //-----------------------------------------------------------------                                                      
        // 函数名:ex_newshake                                       
        // 功 能:解析握手帧数据
        // 参 数:recv ：握手帧数据
        // 返回值:无
        //-----------------------------------------------------------------
        public void ex_newshake(byte[] recv)
        {
            string sTemp = "";
            this.uiProcessBar1.Value = 19 * 100 / 19;            //进度条显示
            this.uiProcessBar1.Text = 19 * 100 / 19 + "%";  //进度百分比显示
            this.uiProcessBar1.Refresh();
            //byte数组转结构体
            try
            {
                softVersion = 2;
                newshakedata = (newshakeData)bytesToStruct(recv, typeof(newshakeData));
                //获取握手帧数据
                uecomType = Encoding.Default.GetString(newshakedata.uecomType).Replace("\0", "");
                mcuType = Encoding.Default.GetString(newshakedata.mcuType).Replace("\0", "");
                uStartAds = newshakedata.uStartAds;    //User程序起始地址
                UserStartAddress = uStartAds;
                uCodeSize = newshakedata.uCodeSize;
                replaceNum = newshakedata.replaceNum;
                reserveNum = newshakedata.reserveNum;
                biosVersion = Encoding.Default.GetString(newshakedata.BIOSVersion).Replace("\0", "");
                userVersion = Encoding.Default.GetString(newshakedata.USERVersion).Replace("\0", "");
            }
            catch (Exception)
            {
                throw;
            }
            //设置设备信息  
            sTemp = mcuType;  //设备信息
            this.txt_updateinfo1.Text = "运行状态：" + sTemp;   //底部提示
            this.txt_updateinfo1.Text = sTemp + "\r\n";     //右侧更新提示区     
            //若保留更新最大字节为0表示目前没有User程序，此时只能进行整体更新
            if (reserveNum == 0)
            {
                txtShow1("可进行GEC中User程序更新\r\n");     //右侧更新提示区  
            }
            btn_autoupdate1.Enabled = true;
        }
        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 函数名称：IoT_recv                                       
        /// 输入参数：无                              
        /// 功  能：（1）读出数据并显示，每接收一帧改变一次颜色（红和绿）
        ///         （2）将数据写入数据库
        /// 返  回：无
        /// 说  明：HCIcom类的DataReceived（数据接收）事件的处理程序
        /// </summary>   
        /// 【20200519】
        ///-----------------------------------------------------------------
        public void IoT_recv()
        {
            byte[] frameData = null;        //用于存储接收帧中的有效数据
            com.DataReceivedEvent -= new HCICom.DataReceived(IoT_recv);    // 先将该事件注销一次
            DateTime dateTimeStart = new DateTime(1970, 1, 1);   //获取时间基准
            string imsiRecv = null;
            try
            {
                //判断是否有数据可取，若无数据，退出；若有读取到imsiRecv、frameData中
                if (com.Read(ref imsiRecv, ref frameData) == false)
                {
                    goto IoT_recv_Err1;
                }
                //更新途中不可点击“连接GEC按钮”
                if (gecLinkEnable == true)
                {
                    btn_uartcheck.Enabled = true;
                }
                else
                {
                    btn_uartcheck.Enabled = false;
                }
                //添加侦听到的IMSI号
                if (!this.ckl_imsi.Items.Contains(imsiRecv))
                {
                    this.ckl_imsi.Items.Add(imsiRecv);
                }
            }
            catch
            {
                goto IoT_recv_exit;
            }
            goto IoT_recv_exit;
        IoT_recv_Err1:
        IoT_recv_exit:
            com.DataReceivedEvent -= new HCICom.DataReceived(IoT_recv);    // 先将该事件注销一次
            com.DataReceivedEvent += new HCICom.DataReceived(IoT_recv);    // 先将该事件注销一次
            return;
        }


        ///-----------------------------------------------------------------
        /// <summary>                                                       
        /// 函数名称：update_recv                                       
        /// 输入参数：无                              
        /// 功  能：//【20220719】用于程序更新的通讯对象
        /// 返  回：无
        /// 说  明：HCIcom类的DataReceived（数据接收）事件的处理程序
        /// </summary>   
        /// 【20200519】
        ///-----------------------------------------------------------------
        public void update_recv()
        {
            byte[] frameData = null;        //用于存储接收帧中的有效数据
            int len;  //获取接收数据长度
            updateCom.DataReceivedEvent -= new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
            string imsiRecv = null;
            try
            {
                //【1】判断是否有数据可取，若无数据，退出；若有读取到imsiRecv、frameData中
                if (updateCom.Read(ref imsiRecv, ref frameData) == false)
                {
                    goto IoT_recv_Err1;
                }

                len = frameData.Length;              //获取数据帧的长度

                //跳转BIOS后接收握手帧数据并解析
                if (len == Marshal.SizeOf(typeof(newshakeData)))
                {
                    this.btn_autoupdate1.Enabled = true; //允许导入hex文件
                    ex_newshake(frameData); //解析握手帧
                }
                if (len == Marshal.SizeOf(typeof(new2shakeData)))
                {
                    this.btn_autoupdate1.Enabled = true; //允许导入hex文件
                    ex_new2shake(frameData); //解析握手帧
                }

                //解析下位机发送过来的更新数据帧接收数组
                if (frameData[0] == 'S')
                {
                    byte[] senddata = null;
                    int islost = 0;
                    //解析数组
                    int[] f = new int[NBsum - 2];
                    for (int n = 0; n < (NBsum - 2); n++)
                    {
                        f[n] = 1;
                    }
                    for (int n = 0; n < 32; n++)
                    {
                        for (int m = 0; m < 8; m++)
                        {
                            //记录下位机未接收到的更新帧帧号
                            if ((frameData[n + 4] & (1 << m)) == 0)
                            {
                                if (n * 8 + m >= (NBsum - 2))
                                {
                                    goto label1;
                                }
                                f[n * 8 + m] = 0;
                                islost = 1;
                            }
                        }
                    }
                label1:
                    //处理下位机接收完成成功的情况
                    if (islost == 0)
                    {
                        back_user = 1;

                        if (BIOS_USER_updataStyle == 1)
                        {
                            byte[] newsenddata = new byte[1];
                            newsenddata[0] = (byte)'E';
                            updateCom.Send(imsiRecv, newsenddata);
                            this.prg_update1.Value = 100;            //进度条显示
                            this.prg_update1.Text = "100" + "%";  //进度百分比显示
                            this.prg_update1.Refresh();
                            txtShow1("更新数据发送完全，即将进入User程序 \r\n");
                            //【20220719】新增倒计时
                            //至此USER更新结束，关闭倒计时
                            //解绑倒计时提示
                            t_countdown_S.Elapsed -= new System.Timers.ElapsedEventHandler(countDown);//到时间的时候执行事件； 
                            countDownSec = 0; //清除倒计时结束
                            lblCountDown.Text = "倒计时提示：关闭";
                            updateCom.DataReceivedEvent -= new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
                            updateCom.DataReceivedEvent += new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
                            //删除已更新的终端IMSI号
                            for (int i = 0; i < this.ckl_imsi.Items.Count; i++)
                            {
                                if (this.ckl_imsi.Items[i].ToString() == imsi1)
                                {
                                    this.ckl_imsi.Items.RemoveAt(i);  //当前勾选的IMSI号
                                }
                            }
                            //可以点击连接GEC按钮
                            gecLinkEnable = true;
                            this.prg_update1.Value = 0;
                            this.prg_update1.Text = "0" + "%";  //进度百分比显示
                            this.prg_update1.Refresh();
                            this.btn_autoupdate1.Enabled = false;
                            this.ckl_imsi.Enabled = true;
                        }
                        else
                        {
                            byte[] newsenddata = new byte[1];
                            newsenddata[0] = (byte)'M';
                            updateCom.Send(imsi1, newsenddata);
                            txtShow1("更新数据发送完全，即将进入bios程序覆盖 \r\n");
                            txtShow1("等待终端回发信息，进行User程序更新 \r\n");
                            //【20220719】新增倒计时
                            //至此BIOS更新结束，关闭倒计时
                            //解绑倒计时提示
                            t_countdown_S.Elapsed -= new System.Timers.ElapsedEventHandler(countDown);//到时间的时候执行事件； 
                            countDownSec = 0; //清除倒计时结束
                            lblCountDown.Text = "倒计时提示：关闭";
                            this.prg_update1.Value = 100;            //进度条显示
                            this.prg_update1.Text = "100" + "%";  //进度百分比显示
                            this.prg_update1.Refresh();
                            //进行USER更新
                            this.User_Update.Checked = true;
                            this.BIOS_update.Checked = false;
                            BIOS_USER_updataStyle = 1;
                            this.prg_update1.Value = 0;
                            this.btn_autoupdate1.Enabled = true;
                            updateCom.DataReceivedEvent -= new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
                            updateCom.DataReceivedEvent += new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
                        }

                        return;
                    }
                    //需要进行重发



                    int supplySendDate = 0; //记录重发总帧数
                    for (int n = 0; n < (NBsum - 2); n++)
                    {
                        if (f[n] == 0)
                        {
                            supplySendDate += 1;
                            //清除上位机记录已发帧帧号标志
                            update.nbclearupdateRecv(n);
                        }
                    }
                    txtShow1("开始丢帧重发 \r\n");
                    //【20220719】新增倒计时
                    //解绑倒计时提示
                    t_countdown_S.Elapsed -= new System.Timers.ElapsedEventHandler(countDown);//到时间的时候执行事件； 
                    countDownSec = 0; //清除倒计时结束
                    Thread.Sleep(1000);
                    int now_index = 0; //重发第一帧多次尝试发送
                    //发送丢失帧
                    for (; ; )
                    {
                        //从update类中取出待发送的数据帧，逐一进行发送
                        if ((senddata = update.getNextFrame()) != null)
                        {
                            NBcur = update.getNextIndex();
                            now_index++;
                            update.nbupdateRecv(NBcur);
                            if (NBcur < NBsum - 2)
                            {
                                byte[] newsenddata = new byte[senddata.Length + 3];
                                Array.Copy(senddata, 0, newsenddata, 3, senddata.Length);
                                newsenddata[0] = (byte)'D';
                                newsenddata[1] = (byte)NBcur;
                                updateCom.Send(imsiRecv, newsenddata);
                                //成功提示
                                //当前帧数据操作成功，进度条显示更新进度
                                this.prg_update1.Value = (NBcur + 1) * 100 / NBsum;            //进度条显示
                                this.prg_update1.Text = (NBcur + 1) * 100 / NBsum + "%";  //进度百分比显示
                                this.prg_update1.Refresh();
                                txtShow1("当前第" + (NBcur).ToString() + "/" + (NBsum - 3).ToString() + "帧 \r\n");     //右侧更新提示区  
                                Thread.Sleep(2000);
                            }

                            if (now_index == supplySendDate)
                            {
                                //再次发送完成，等待下位机回发接收情况
                                txtShow1("正在进行数据检测，请稍等\r\n");
                                int time = (NBsum * 5);
                                txtShow1("如果超过" + time.ToString() + "秒无任何提示信息则说明终端出现断电，信号中断等故障，请勿关闭软件，等待终端回发信息\r\n");     //右侧更新提示区
                                //【20220719】新增倒计时
                                countDownSec = time; //设置倒计时为time秒
                                t_countdown_S.Elapsed -= new System.Timers.ElapsedEventHandler(countDown);//到时间的时候执行事件； 
                                t_countdown_S.Elapsed += new System.Timers.ElapsedEventHandler(countDown);//到时间的时候执行事件； 
                                t_countdown_S.AutoReset = true;//设置是执行一次（false）还是一直执行(true)； 
                                t_countdown_S.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件；
                                updateCom.DataReceivedEvent -= new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
                                updateCom.DataReceivedEvent += new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
                                return;
                            }
                        }

                    }
                }
            }
            catch
            {
                goto IoT_recv_exit;
            }
            goto IoT_recv_exit;
        IoT_recv_Err1:
        IoT_recv_exit:

            return;
        }


        //======================================================================
        //函数名称：btn_uartcheck_Click
        //函数返回：无
        //参数说明：无
        //功能概要：“连接GEC”按钮点击事件，重新连接终端
        //======================================================================
        private void btn_uartcheck_Click(object sender, EventArgs e)
        {
            //gecLinkEnable = false; // 禁止连接GEC按钮
            btn_uartcheck.Enabled = false;
            //（1）勾选的要更新的IMSI号
            for (int i = 0; i < this.ckl_imsi.Items.Count; i++)
            {
                if (this.ckl_imsi.GetItemChecked(i))
                {
                    imsi1 = this.ckl_imsi.Items[i].ToString();  //当前勾选的IMSI号
                }
            }
            //（2）发送跳转帧（使得下位机从USER跳转回BIOS）
            string d1 = "0123456789ABCDEF"; //设备序列号以及软件版本号初值
            head = System.Text.Encoding.Default.GetBytes(d1);
            string d2 = "20200718"; //软件版本号
            byte[] byteArray2 = System.Text.Encoding.Default.GetBytes(d2);
            //（2.1）跳转帧数据赋值
            byte[] send_data = new byte[26 + 24 + 16];
            byte[] send1_data = new byte[26];
            Array.Copy(head, 0, send_data, 0, head.Length);
            send_data[16] = (byte)'T';
            send_data[17] = (byte)'0';
            Array.Copy(byteArray2, 0, send_data, 18, byteArray2.Length);
            byte[] sever_ip = new byte[16];
            byte[] sever_ip1 = System.Text.Encoding.Default.GetBytes(textBox1.Text);
            byte[] sever_ip2 = System.Text.Encoding.Default.GetBytes(textBox4.Text);
            //固定更新端口为23334
            byte[] port1 = System.Text.Encoding.Default.GetBytes(textBox3.Text);
            byte[] imsi_1 = System.Text.Encoding.Default.GetBytes(imsi1);
            //服务器地址赋值
            for (int a = 0; a < 16; a++)
            {
                if (a < sever_ip1.Length)
                    sever_ip[a] = sever_ip1[a];

                else
                    sever_ip[a] = (byte)'\0';
            }
            //服务器端口号赋值
            byte[] port = new byte[8];
            for (int a = 0; a < 8; a++)
            {
                if (a < port1.Length)
                    port[a] = port1[a];
                else
                    port[a] = (byte)'\0';
            }
            //设备IMSI号赋值
            byte[] imsi_save = new byte[16];
            for (int i = 0; i < 16; i++)
            {
                if (i < imsi_1.Length)
                {
                    imsi_save[i] = imsi_1[i];
                }
                else
                    imsi_save[i] = (byte)'\0';
            }
            Array.Copy(sever_ip, 0, send_data, 26, 16);
            Array.Copy(port, 0, send_data, 42, 8);
            Array.Copy(imsi_save, 0, send_data, 50, 16);
            //BIOS_USER_updataStyle 0：BIOS更新；1：USER更新
            if (User_Update.Checked == true)
            {
                BIOS_USER_updataStyle = 1;
            }
            else
            {
                BIOS_USER_updataStyle = 0;

            }
            //发送跳转帧
            com.Send(imsi1, send_data);   //复位帧
            Array.Copy(send_data, 0, resenddata, 0, send_data.Length);
            resenddatalen = send_data.Length;

            //绑定云侦听事件
            updateCom.DataReceivedEvent -= new HCICom.DataReceived(update_recv);
            updateCom.DataReceivedEvent += new HCICom.DataReceived(update_recv);
            //提示信息
            txt_updateinfo1.Text += "系统会复位初始化模组，若出现以下情况请重新打开更新程序\r\n" +
                "1：1分30s后还未显示更新提示\r\n2：1分30s内“自动更新”按钮仍是不可点击状态\r\n";
            //【20220719】新增倒计时
            //开启定时器，以便提示用户倒计时
            countDownSec = 90; //设置倒计时为90s
            t_countdown_S.Elapsed -= new System.Timers.ElapsedEventHandler(countDown);//先解绑
            t_countdown_S.Elapsed += new System.Timers.ElapsedEventHandler(countDown);//到时间的时候执行事件； 
            t_countdown_S.AutoReset = true;//设置是执行一次（false）还是一直执行(true)； 
            t_countdown_S.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件；
            //更新进度条
            this.uiProcessBar1.Value = 17 * 100 / 17;            //进度条显示
            this.uiProcessBar1.Text = 17 * 100 / 17 + "%";  //进度百分比显示
            this.uiProcessBar1.Refresh();  //更新进度条
            this.ckl_imsi.Enabled = false; //不允许更改选中IMSI号
            this.btn_uartcheck.Enabled = false; //不允许重新连接GEC
            this.btn_uartcheck.Symbol = 61475; //修改图标
        }


        ///-----------------------------------------------------------------                                                      
        /// 函数名:p_OutputDataReceived                                       
        /// 功  能:委托函数           
        /// 参  数:自动生成
        /// 返回值:无
        ///-----------------------------------------------------------------
        private void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
                // 4. 异步调用，需要invoke（获取操作控件的线程）  
                this.Invoke(ReadStdOutput, new object[] { e.Data });
        }

        ///-----------------------------------------------------------------                                                      
        /// 函数名:p_ErrorDataReceived                                     
        /// 功  能:委托函数           
        /// 参  数:自动生成
        /// 返回值:无
        ///-----------------------------------------------------------------
        private void p_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
                this.Invoke(ReadErrOutput, new object[] { e.Data });
        }

        ///-----------------------------------------------------------------                                                      
        /// 函数名:compilerCommand                                       
        /// 功  能:打开dos命令窗体并输入命令执行frpc.exe应用程序            
        /// 参  数:frpPath：frpc.ini文件的绝对路径,cmd:需要输入到dos窗体的命令
        /// 返回值:无
        ///-----------------------------------------------------------------
        private void compilerCommand(string frpPath, string cmd)
        {
            try
            {
                Process p;
                string cmdStr = "cd " + frpPath;
                //调用cmd命令窗体开始编译
                p = new Process();
                //Process类有一个StartInfo属性，这个是ProcessStartInfo类，包括了一些属性和方法，
                //设定程序名
                p.StartInfo.FileName = "cmd.exe";
                //关闭Shell的使用
                p.StartInfo.UseShellExecute = false;
                //重定向标准输入
                p.StartInfo.RedirectStandardInput = true;
                //重定向标准输出
                p.StartInfo.RedirectStandardOutput = true;
                //重定向错误输出
                p.StartInfo.RedirectStandardError = true;
                //设置不显示窗口
                p.StartInfo.CreateNoWindow = true;

                p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
                p.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);

                //上面几个属性的设置是比较关键的一步。
                p.Start();
                p.BeginOutputReadLine();
                p.BeginErrorReadLine();
                p.StandardInput.AutoFlush = true;           //写入命令，输入要执行的命令
                p.StandardInput.WriteLine(cmdStr);
                Thread.Sleep(1000);
                p.StandardInput.WriteLine(cmd);
                Thread.Sleep(1000);
                //MessageBox.Show("FRP 客户端启动成功");
                p.Close();//结束进程
                p.Dispose();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        public void cancel_update(object source, System.Timers.ElapsedEventArgs e)
        {
            if (updata_flag == 0)
            {
                byte[] newsenddata = new byte[1];
                newsenddata[0] = (byte)'M';
                com.Send(imsi1, newsenddata);
                this.ckl_imsi.Enabled = true;
                txt_updateinfo1.Text = string.Empty;
                back_user = 1;
            }
        }


        ///-----------------------------------------------------------------                                                      
        /// 函数名:uiSymbolButton1_Click                                       
        /// 功  能:开启侦听          
        /// 参  数:无
        /// 返回值:无
        ///-----------------------------------------------------------------
        private void uiSymbolButton1_Click(object sender, EventArgs e)
        {
            if (this.uiSymbolButton1.Text == "开启侦听")
            {

                com.HCIComTarget = "local" + ":" + textBox2.Text;     //设置属性（目标地址及端口）
                updateCom.HCIComTarget = "local" + ":" + textBox3.Text;     //设置属性（目标地址及端口）
                //若与将要侦听的imsi号建立连接失败，在界面下方提示错误
                //将要侦听的imsi号数组g_IMSI作为实参
                if (com.Init(null) != 0)
                {
                    txt_updateinfo1.Text += "开启终端数据侦听失败\r\n";
                }
                //若与将要侦听的imsi号建立连接成功，在界面下方给出提示信息
                else
                {
                    txt_updateinfo1.Text += "开启终端数据侦听成功\r\n";
                    socket_flag = 1;
                }
                if (updateCom.Init(null) != 0)
                {
                    txt_updateinfo1.Text += "开启更新端口侦听失败\r\n";
                }
                //若与将要侦听的imsi号建立连接成功，在界面下方给出提示信息
                else
                {
                    txt_updateinfo1.Text += "开启更新端口侦听成功\r\n";
                    socket_flag = 1;
                }



                //【5】注册接收数据事件处理程序.
                com.DataReceivedEvent -= new HCICom.DataReceived(IoT_recv);//【20200527】在注册IoT_recv事件之前，先将该事件注销一次
                com.DataReceivedEvent += new HCICom.DataReceived(IoT_recv);
                updateCom.DataReceivedEvent -= new HCICom.DataReceived(update_recv);//【20220719】注册更新侦听端口事件
                updateCom.DataReceivedEvent += new HCICom.DataReceived(update_recv);

                this.uiSymbolButton1.Text = "关闭侦听";
                this.uiSymbolButton1.Symbol = 61453;

            }
            else
            {
                if (socket_flag == 1)
                {
                    //关闭侦听
                    com.closelink();
                    updateCom.closelink();
                    socket_flag = 0;
                }
                this.uiSymbolButton1.Text = "开启侦听";
                this.uiSymbolButton1.Symbol = 61598;
            }

        }
        ///-----------------------------------------------------------------                                                      
        /// 函数名:uiSymbolButton2_Click                                       
        /// 功  能:取消更新         
        /// 参  数:无
        /// 返回值:无
        ///-----------------------------------------------------------------
        private void uiSymbolButton2_Click(object sender, EventArgs e)
        {
            byte[] newsenddata = new byte[1];
            newsenddata[0] = (byte)'M';
            com.Send(imsi1, newsenddata);
            back_user = 1;
            this.ckl_imsi.Enabled = true;
            txt_updateinfo1.Text = string.Empty;
        }

        ///-----------------------------------------------------------------                                                      
        /// 函数名:countDown                                     
        /// 功  能:【20220719】新增倒计时     
        /// 参  数:无
        /// 返回值:无
        ///-----------------------------------------------------------------
        public void countDown(object source, System.Timers.ElapsedEventArgs e)
        {
            //倒计时已经结束
            if (countDownSec < 0)
            {
                lblCountDown.Text = "倒计时提示：结束";
                //【5】注册接收数据事件处理程序.
                com.DataReceivedEvent -= new HCICom.DataReceived(IoT_recv);//【20200527】在注册IoT_recv事件之前，先将该事件注销一次
                com.DataReceivedEvent += new HCICom.DataReceived(IoT_recv);
                updateCom.DataReceivedEvent -= new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
                updateCom.DataReceivedEvent += new HCICom.DataReceived(update_recv);    // 先将该事件注销一次
                countDownSec = 60 * 20;
            }
            else
            {
                countDownSec -= 1; //秒数减一
                lblCountDown.Text = "倒计时提示：" + countDownSec.ToString() + "s"; ;
            }
        }
        public void ex_new2shake(byte[] recv)
        {
            string sTemp = "";
            this.uiProcessBar1.Value = 19 * 100 / 19;            //进度条显示
            this.uiProcessBar1.Text = 19 * 100 / 19 + "%";  //进度百分比显示
            this.uiProcessBar1.Refresh();
            //byte数组转结构体
            try
            {
                softVersion = 2;
                new2shakedata = (new2shakeData)bytesToStruct(recv, typeof(new2shakeData));
                //获取握手帧数据
                uecomType = Encoding.Default.GetString(new2shakedata.uecomType).Replace("\0", "");
                mcuType = Encoding.Default.GetString(new2shakedata.mcuType).Replace("\0", "");
                uStartAds = new2shakedata.uStartAds;    //User程序起始地址
                UserStartAddress = uStartAds;
                uCodeSize = new2shakedata.uCodeSize;
                replaceNum = new2shakedata.replaceNum;
                reserveNum = new2shakedata.reserveNum;
                biosVersion = Encoding.Default.GetString(new2shakedata.BIOSVersion).Replace("\0", "");
                //userVersion = Encoding.Default.GetString(newshakedata.USERVersion).Replace("\0", "");
            }
            catch (Exception)
            {
                throw;
            }
            //设置设备信息  
            sTemp = mcuType;  //设备信息                                                     // this.lbl_uartstate.Text += "\r\n" + biosVersion;
            this.txt_updateinfo1.Text = "运行状态：" + sTemp;   //底部提示
            this.txt_updateinfo1.Text = sTemp + "\r\n";     //右侧更新提示区     
            //this.txtUserVersion.Text = userVersion;

            //若保留更新最大字节为0表示目前没有User程序，此时只能进行整体更新
            if (reserveNum == 0)
            {
                txtShow1("可进行GEC中User程序更新\r\n");     //右侧更新提示区  
            }
            btn_autoupdate1.Enabled = true;
        }

        private void textBox3_TextChanged(object sender, EventArgs e)
        {

        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void uiLabel2_Click(object sender, EventArgs e)
        {

        }
    }
}


